package pl.edu.romanow.dispatch


import net.liftweb.http._
import js.jquery.JqJsCmds
import js.{JsCmds, JsCmd}
import pl.edu.romanow.util.{HTMLUtil, ResponseCodes, HTTPUtil, PopupUtil}
import net.liftweb.common.{Logger, Full, Empty, Box}
import java.net.URLEncoder
import pl.edu.romanow.model.{VisualError, Facebook, FacebookUtil, MyProps}
import xml.{NodeSeq, Text}


/**
 * Created by IntelliJ IDEA.
 * User: jar
 * Date: 25.03.12
 * Time: 11:33
 * To change this template use File | Settings | File Templates.
 */

object FacebookDispatch extends Logger {

  val facebookCallbackUrl: List[String] = "facebook" :: "callback" :: Nil
  val facebookRedirectUrl: List[String] = "facebook" :: "redirect" :: Nil

  def dispatch: LiftRules.DispatchPF = {
    case req@Req("facebook" :: "callback" :: Nil, _, _) => () => facebookCallback(req)
    case req@Req("facebook" :: "redirect" :: Nil, _, GetRequest) => () => facebookRedirect(req)
  }

  def basicPermission = "user_about_me,friends_about_me,offline_access,friends_likes,friends_interests,friends_groups"

  //auth
  def ajaxRedirect(permissions: String = basicPermission, afterSuccess: Box[JsCmd] = Empty): JsCmd = {

    val state = System.nanoTime().toString

    currentState(Full(state))

    PopupUtil.openInPopup(
      url = "/" + facebookRedirectUrl.mkString("/"),
      afterRefresh = afterSuccess,
      title = permissions
    )
  }

  def processError(msg: String, datas: Any*): Box[LiftResponse] = {
    error(msg)
    error(datas.mkString(" "))
    Full(RedirectResponse(PopupUtil.popupRefreshRedirectUrl));
  }


  //first step in oauth
  def facebookRedirect(req: Req): Box[LiftResponse] = {


    val state = System.nanoTime().toString

    currentState(Full(state))

    val url =
      ("https://www.facebook.com/dialog/oauth?" +
        "client_id=%s" +
        "&redirect_uri=%s" +
        "&scope=%s&state=%s")
        .format(MyProps.FacebookClientId,
        req.hostAndPath + "/facebook/callback",
        basicPermission,
        state
      )
    Full(RedirectResponse(url))
  }


  //2nd step in oauth
  def facebookCallback(req: Req): Box[LiftResponse] = {
    def codeForAccessTokenUrl(code: String) = "https://graph.facebook.com/oauth/access_token" +
      "?client_id=%s&redirect_uri=%s&client_secret=%s&code=%s"
        .format(MyProps.FacebookClientId, req.hostAndPath + "/facebook/callback", MyProps.FacebookClientSecret, code)


    (S.param("state"), S.param("code"), S.param("error")) match {

      case (Full(state), Full(code), Empty) =>
        currentState.is match {
          case Full(iState) if iState == state =>
            HTTPUtil.readFromGetRequest(codeForAccessTokenUrl(code)) match {
              case (ResponseCodes.OK, Full(data)) =>
                FacebookUtil.access_token(data) match {
                  case Full(token) =>
                    Facebook.currentAccessToken(Full(token));

                    val url = PopupUtil.popupRefreshRedirectUrl
                    return Full(RedirectResponse(url))
                  case _ => processError("when parsing access token", data)
                }
                Empty
              case (error, Full(data)) =>
                processError("when requestinf acccess token", error, data)
              case (error, _) =>
                processError("when requestinf acccess token", error)
            }
          case _ =>
            processError("no or bad state", state, currentState.is)
        }
      case (_, _, Full(error)) =>
        processError(error)
      case _ => processError("Unknow error", req.params.toArray: _*);
    }
  }

  def facebookError(job: FacebookJob, code: Int, data: String = ""): FacebookError = {
    error("when processig: %s code %s message %s".format(job.name, code.toString, data))
    FacebookError(code, data)
  }


  object currentState extends SessionVar[Box[String]](Empty)

  trait FacebookJobReq {
    val job: FacebookJob
    val token: String
  }

  object FacebookJobReq {
    def apply(jobA: FacebookJob, tokenA: String) = new FacebookJobReq {
      val job: FacebookJob = jobA
      val token: String = tokenA
    }
  }

  def processFacebookJobReq(req: FacebookJobReq, success: => JsCmd): JobStatus = {

    val token = req.token
    val job = req.job



    HTTPUtil.readFromGetRequest(job.createUrl(token)) match {
      case (ResponseCodes.OK, Full(data)) => job.handleResponse(data) match {
        case Empty =>
          info("Complete " + job.name)
          FacebookSuccess(success)
        case Full(msg) => facebookError(job, 0, msg);
        case _ => facebookError(job, 0, "UnkownError")
      }
      case (error, Full(data)) => facebookError(job, error, data)
      case (error, _) => facebookError(job, error)
    }
  }


  def processFacebookJob(job: FacebookJob, success: => JsCmd): JobStatus = {

    val token: String = Facebook.currentAccessToken.is match {
      case Full(t) => t;
      case _ => return FacebookRedirect(job.permission, Full(HTMLUtil.ajaxCall(
        processFacebookJob(job, success).toJsCmd())))
    }
    processFacebookJobReq(FacebookJobReq(job, token), success)
  }

  trait JobStatus extends VisualError

  case class FacebookSuccess(success: JsCmd) extends JobStatus {
    override def toJsCmd(): JsCmd = success

    override def toNodeSeq: NodeSeq = NodeSeq.Empty
  }

  case class FacebookError(code: Int, reason: String) extends JobStatus {
    override def toJsCmd(): JsCmd =
      JqJsCmds.DisplayMessage("facebook_error", toNodeSeq, 5000, 2000)

    def toNodeSeq: NodeSeq = Text("facebook error code: %s message: %s".format(code.toString, reason))
  }

  case class FacebookRedirect(permissions: String, success: Box[JsCmd]) extends JobStatus {
    override def toJsCmd(): JsCmd = ajaxRedirect(permissions, success)

    //TODO wizual part
    def toNodeSeq: NodeSeq = Text("GRATE BEUTIFUL BUTTON");
  }

}


